package service

import (
	"context"
	"errors"

	"gitee.com/jn-shao/common"
	netv1 "k8s.io/api/networking/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"

	"routeSrv/domain/model"
	"routeSrv/domain/repository"
	"routeSrv/proto/routeSrv"
)

// 这里是接口类型
type IRouteDataService interface {
	AddRoute(*model.Route) (int64, error)
	DeleteRoute(int64) error
	UpdateRoute(*model.Route) error
	FindRouteByID(int64) (*model.Route, error)
	FindRouteList(Route *model.Page) (int64, []model.Route, error)
	CreateToK8s(*routeSrv.RouteInfo) error
	DeleteFromK8s(*model.Route) error
	UpdateToK8s(*routeSrv.RouteInfo) error
}

type RouteDataService struct {
	// 注意：这里是 IRouteRepository 类型
	RouteRepository repository.IRouteRepository
	// 相当于kubectl与k8s交互
	K8sClientSet *kubernetes.Clientset
	// Ingress
	ingress *netv1.Ingress
}

// 创建
// 注意：返回值 IRouteDataService 接口类型
func NewRouteDataService(routeRepository repository.IRouteRepository, clientSet *kubernetes.Clientset) IRouteDataService {
	return &RouteDataService{
		RouteRepository: routeRepository,
		K8sClientSet:    clientSet,
		ingress:         &netv1.Ingress{},
	}
}

// 添加 Route
func (p *RouteDataService) AddRoute(route *model.Route) (int64, error) {
	return p.RouteRepository.CreateRoute(route)
}

// 删除 Route
func (p *RouteDataService) DeleteRoute(routeID int64) error {
	return p.RouteRepository.DeleteRouteByID(routeID)
}

// 更新 Route
func (p *RouteDataService) UpdateRoute(route *model.Route) error {
	return p.RouteRepository.UpdateRoute(route)
}

// 单个 Route 查找
func (p *RouteDataService) FindRouteByID(routeID int64) (*model.Route, error) {
	return p.RouteRepository.FindRouteByID(routeID)
}

// 查找所有 Route
func (p *RouteDataService) FindRouteList(page *model.Page) (int64, []model.Route, error) {
	return p.RouteRepository.FindRouteList(page)
}

// 创建 Route 到 k8s 中
func (p *RouteDataService) CreateToK8s(routeInfo *routeSrv.RouteInfo) error {
	p.setIngress(routeInfo)
	// 查看 ingresses 是否存在
	if _, err := p.K8sClientSet.NetworkingV1().Ingresses(routeInfo.RouteNamespace).Get(context.TODO(), routeInfo.RouteName, metav1.GetOptions{}); err != nil {
		// 不存在，创建 ingresses
		if _, err = p.K8sClientSet.NetworkingV1().Ingresses(routeInfo.RouteNamespace).Create(context.TODO(), p.ingress, metav1.CreateOptions{}); err != nil {
			common.Error(err)
			return err
		}
		common.Info("创建成功")
		return nil

	} else {
		// 存在，可以写自己的业务逻辑
		common.Error("Ingress " + routeInfo.RouteName + "已经存在")
		return errors.New("Ingress " + routeInfo.RouteName + " 已经存在")
	}
}

// 删除 Route
func (p *RouteDataService) DeleteFromK8s(route *model.Route) error {
	// 删除 ingress
	if err := p.K8sClientSet.NetworkingV1().Ingresses(route.RouteNamespace).Delete(context.TODO(), route.RouteName, metav1.DeleteOptions{}); err != nil {
		common.Error(err)
		//写自己的业务逻辑
		return err
	}
	common.Info(route.RouteName + " 删除成功")
	return nil
}

// 更新 Route
func (p *RouteDataService) UpdateToK8s(routeInfo *routeSrv.RouteInfo) error {
	p.setIngress(routeInfo)
	// 查看 ingress 是否存在
	if ingress, err := p.K8sClientSet.NetworkingV1().Ingresses(routeInfo.RouteNamespace).Get(context.TODO(), routeInfo.RouteName, metav1.GetOptions{}); err != nil {
		common.Error(err)
		return errors.New("Ingress " + routeInfo.RouteName + " 不存在请先创建")
	} else {
		// 如果存在
		p.ingress.SetResourceVersion(ingress.ResourceVersion)
		if _, err = p.K8sClientSet.NetworkingV1().Ingresses(routeInfo.RouteNamespace).Update(context.TODO(), p.ingress, metav1.UpdateOptions{}); err != nil {
			common.Error(err)
			return err
		}
	}
	common.Info(routeInfo.RouteName + " 更新成功")
	return nil
}

func (p *RouteDataService) setIngress(info *routeSrv.RouteInfo) {
	route := &netv1.Ingress{}
	//设置路由
	route.TypeMeta = metav1.TypeMeta{
		Kind:       "Ingress",
		APIVersion: "networking.k8s.io/v1",
	}
	//设置路由基础信息
	route.ObjectMeta = metav1.ObjectMeta{
		Name:      info.RouteName,
		Namespace: info.RouteNamespace,
		Labels: map[string]string{
			"app-name": info.RouteName,
			"author":   "SHao",
		},
		Annotations: map[string]string{
			"k8s/generated-by": "go-paas",
		},
	}
	//使用 ingress-nginx
	className := "nginx"
	//设置路由 spec 信息
	route.Spec = netv1.IngressSpec{
		IngressClassName: &className,
		//默认访问服务
		DefaultBackend: nil,
		//如果开启https这里要设置
		TLS:   nil,
		Rules: p.getIngressPath(info),
	}
	p.ingress = route
}

// 根据info信息获取path路径
func (p *RouteDataService) getIngressPath(info *routeSrv.RouteInfo) (path []netv1.IngressRule) {
	//1.设置host
	pathRule := netv1.IngressRule{Host: info.RouteHost}
	//2.设置Path
	var ingressPath []netv1.HTTPIngressPath
	for _, v := range info.RoutePath {
		pathType := netv1.PathTypePrefix
		ingressPath = append(ingressPath, netv1.HTTPIngressPath{
			Path:     v.RoutePathName,
			PathType: &pathType,
			Backend: netv1.IngressBackend{
				Service: &netv1.IngressServiceBackend{
					Name: v.RouteBackendService,
					Port: netv1.ServiceBackendPort{
						Number: v.RouteBackendServicePort,
					},
				},
			},
		})
	}

	//3.赋值 Path
	pathRule.IngressRuleValue = netv1.IngressRuleValue{HTTP: &netv1.HTTPIngressRuleValue{Paths: ingressPath}}
	path = append(path, pathRule)
	return
}
